#pragma once
#include "ir.h"
#include <vector>
#include <variant>
#include <string>

namespace code
{
    const int INT12_MIN = -2048;
    const int INT12_MAX = 2047;

    typedef std::variant<std::string, int> Operand;

    enum Opcode
    {   
        // 直接代码生成时用到的指令类型  
        OP_ADD, OP_ADDI, OP_SUB, OP_MUL, OP_DIV, OP_REM,
        OP_SNE, OP_SEQ, OP_SGT, OP_SLT, OP_SGE, OP_SLE, 
        OP_AND, OP_OR, 
        OP_LW, OP_SW, OP_LI, OP_MV,
        OP_BNEZ, OP_J, OP_RET, OP_CALL,
        // 替换伪指令时需要的额外的指令类型
        OP_XOR, OP_XORI, OP_SLTU, OP_SLTIU, 
        OP_LUI, OP_BNE, OP_JAL, OP_JALR
    };

    const std::vector<Opcode> PSEUDO_OPCODE = 
    {
        OP_SNE, OP_SEQ, OP_SGT, OP_SGE, OP_SLE, 
        OP_LI, OP_MV, 
        OP_BNEZ, OP_J, OP_RET, OP_CALL
    };

    struct Instruction
    {
        std::string ToString();
        Opcode op;
        std::vector<Operand> operands;
    };

    struct Block
    {
        std::string ToString();
        std::string label;
        std::vector<Instruction> instrs;
    };

    struct Function
    {
        std::string ToString();
        std::string name;
        int stack_size;
        std::vector<Block> blocks;
    };

    struct Code
    {
        std::string ToString();
        std::vector<Function> funcs;
    };

    class CodeGenerator
    {
    public:
        Code Generate(ir::IR& ir);
    private:
        // 分级的代码生成
        Function Generate(ir::Function& ir_func);
        Block Generate(ir::Block& ir_block);
        std::vector<Instruction> Generate(ir::Instruction& ir_instr);

        // 其他辅助函数
        void ReplaceLabel(Block& block, std::string& func_name);
        void ReplacePseudoInstr(Code& code);
        std::vector<bool> ComputeUsedReg(ir::Function& ir_func);
        void ReplaceParam(Function& func, std::vector<std::string>& params);
        void ReplaceStackAddr(
            Function& func, std::vector<ir::Instruction>& ir_alloc_instrs);
        void StoreLoadCalleeSaved(Function& func, std::vector<bool>& used_regs);
        void ModifySpRaFp(Function& func, std::vector<bool>& used_regs);
        std::vector<Instruction> GenerateBinaryInstr(ir::Instruction& ir_instr);
        std::vector<Instruction> GenerateLoadInstr(ir::Instruction& ir_instr);
        std::vector<Instruction> GenerateStoreInstr(ir::Instruction& ir_instr);
        std::vector<Instruction> GenerateMvInstr(ir::Instruction& ir_instr);
        std::vector<Instruction> GenerateBrInstr(ir::Instruction& ir_instr);
        std::vector<Instruction> GenerateJmpInstr(ir::Instruction& ir_instr);
        std::vector<Instruction> GenerateRetInstr(ir::Instruction& ir_instr);
        std::vector<Instruction> GenerateCallInstr(ir::Instruction& ir_instr);
        std::vector<Instruction> InsertLiInstr(ir::Instruction& ir_instr);
        std::vector<Instruction> InsertStoreLoadRegInstr(int reg_index, int offset);
    };
}